﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
using System.Web.Mvc;

namespace planerka.Common
{
    // http://www.notesfor.net/post/2010/06/28/AspNet-custom-ActionResult-CSV.aspx
    // http://snippets.crisp-studio.com/view/122/generaciya-csv-iz-aspnet-mvc-prilozheniya

    public sealed class exportToCSV<T> : System.Web.Mvc.FileResult
    {
        private IEnumerable<T> collection;
        private String[] columnsToExclude;
        private string separator;
        private bool useQuotes;

        public exportToCSV(IEnumerable<T> collection, string separator = ";", bool useQuotes = false, params String[] columnsToExclude)
            : base("text/csv")
        {
            this.collection = collection;
            this.columnsToExclude = columnsToExclude;
            this.separator = separator;
            this.useQuotes = useQuotes;
        }

        protected override void WriteFile(HttpResponseBase response)
        {
            System.IO.Stream outputStream = response.OutputStream;
            using (MemoryStream mstream = new MemoryStream())
            {
                WriteObject(mstream, System.Text.Encoding.Default);
                outputStream.Write(mstream.GetBuffer(), 0, (int)mstream.Length);
            }
        }

        // сохранить в файл
        public void WriteFileToDisk(string fullPath)
        {
            // decode a file stream using UTF-8
            // http://stackoverflow.com/questions/876399/decode-a-file-stream-using-utf-8

            using (MemoryStream mstream = new MemoryStream())
            {
                WriteObject(mstream, System.Text.Encoding.UTF8);
                //FileOptions fo = new FileOptions();

                FileStream file = new FileStream(fullPath, FileMode.Create, System.IO.FileAccess.Write);
                mstream.WriteTo(file);
                mstream.Close();

                file.Close();
                //outputStream.Write(mstream.GetBuffer(), 0, (int)mstream.Length);
                //System.IO.File.WriteAllBytes((HttpContext.Current.Server.MapPath("~/App_Data/backupTransactions.xlsx"), sheet);
            }

        }

        private void WriteObject(Stream stream, System.Text.Encoding enco)// = System.Text.Encoding.Default)
        {
            // We will follow the recommandations stated in this article
            // http://www.commentcamarche.net/faq/sujet-7273-exporter-a-coup-sur-du-csv
            // - embedding all values in quotes
            // - double quote literal values
            // - add \n\r between each lines.

            StreamWriter writer = new StreamWriter(stream, enco);//System.Text.Encoding.Default);

            // Render columns
            Type modelType = typeof(T);
            List<ModelMetadata> metadatas = ModelMetadataProviders.Current.GetMetadataForProperties(null, modelType).ToList();

            for (int i = 0; i < metadatas.Count; )
            {
                if (Array.IndexOf<String>(columnsToExclude, metadatas[i].PropertyName) != -1)
                    metadatas.RemoveAt(i);
                else
                {
                    WriteValue(writer, metadatas[i].DisplayName ?? metadatas[i].PropertyName, separator, useQuotes);
                    i++;
                }
            }

            writer.WriteLine();
            // Render data
            var en = collection.GetEnumerator();

            // Start of fix				
            while (en.MoveNext())
            {
                ModelMetadata mprop = ModelMetadataProviders.Current.GetMetadataForType(() => en.Current, modelType);
                foreach (ModelMetadata prop in mprop.Properties)
                {
                    if (Array.IndexOf<String>(columnsToExclude, prop.PropertyName) == -1)
                    {
                        WriteValue(writer, prop.SimpleDisplayText ?? String.Empty, separator, useQuotes);
                    }
                }
                writer.WriteLine();
            }
            // End of fix

            writer.Flush();
        }

        private static void WriteValue(
            StreamWriter writer,
            String literal,
            string separator,
            bool useQuotes)
        {
            // Enclose values in quote
            if (useQuotes)
                writer.Write("\"");

            string line = literal.Replace("\"", "\"\"");
            writer.Write(line);

            if (useQuotes)
                writer.Write("\"");

            writer.Write(separator);
        }
    }
}
